// Author: wsfuyibing <682805@qq.com>
// Date: 2025-04-11

package gen_model

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	regexMatchFirstLowerLetter    = regexp.MustCompile(`^([a-z])`)
	regexMatchFirstUpperLetter    = regexp.MustCompile(`^[A-Z]`)
	regexMatchUpperLetter         = regexp.MustCompile(`^[A-Z]`)
	regexMatchUnderlineMulti      = regexp.MustCompile(`_+`)
	regexMatchUnderlineWithLetter = regexp.MustCompile(`_+(\S)`)
	regexMatchUnderlineHeads      = regexp.MustCompile(`^_+`)
	regexMatchFieldType           = regexp.MustCompile(`^([_a-zA-Z0-9]+)`)
	regexMatchFieldTypeUnsigned   = regexp.MustCompile(`unsigned`)
)

type (
	Column struct {
		Field   Field      `gorm:"Field"`
		Type    FieldType  `gorm:"Type"`
		Key     FieldKey   `gorm:"Key"`
		Null    FieldNull  `gorm:"Null"`
		Extra   FieldExtra `gorm:"Extra"`
		Comment Comment    `gorm:"Comment"`
		Default string     `gorm:"Default"`
	}
	Comment    string
	Field      string
	FieldExtra string
	FieldKey   string
	FieldType  string
	FieldNull  string
	Table      struct {
		Name    Name      `gorm:"Name"`
		Engine  string    `gorm:"Engine"`
		Comment Comment   `gorm:"Comment"`
		Columns []*Column `gorm:"-"`
	}
)

// +---------------------------------------------------------------------------+
// | Field                                                                     |
// +---------------------------------------------------------------------------+

func (o Field) Camel() (str string) {
	// Read origin name.
	str = o.String()

	// Replace underscore with upper letter.
	//
	// `user_info` -> `userInfo`
	str = regexMatchUnderlineWithLetter.ReplaceAllStringFunc(str, func(s string) string {
		if m := regexMatchUnderlineWithLetter.FindStringSubmatch(s); len(m) > 0 {
			return strings.ToUpper(m[1])
		}
		return ""
	})

	// Replace underscore with upper letter.
	//
	// `userInfo` -> `UserInfo`
	str = regexMatchFirstLowerLetter.ReplaceAllStringFunc(str, func(s string) string {
		return strings.ToUpper(s)
	})
	return
}

func (o Field) Snake() (str string) {
	// Read origin name.
	str = o.String()

	// Replace upper letter.
	//
	// `UserInfo` -> `_user_info`
	str = regexMatchUpperLetter.ReplaceAllStringFunc(str, func(s string) string {
		return "_" + strings.ToLower(s)
	})

	// Replace underscore with upper letter.
	//
	// `_user_info` -> `user_info`
	str = regexMatchUnderlineHeads.ReplaceAllString(str, "")
	return
}

func (o Field) String() string { return string(o) }

// +---------------------------------------------------------------------------+
// | FieldKey                                                                  |
// +---------------------------------------------------------------------------+

func (o FieldKey) IsPrimary() bool { return o.String() == "PRI" }

func (o FieldKey) String() string { return strings.ToUpper(string(o)) }

// +---------------------------------------------------------------------------+
// | FieldNull                                                                 |
// +---------------------------------------------------------------------------+

func (o FieldNull) Yes() bool { return o.String() == "YES" }

func (o FieldNull) String() string {
	return strings.ToUpper(string(o))
}

// +---------------------------------------------------------------------------+
// | FieldType                                                                 |
// +---------------------------------------------------------------------------+

// Export
// generate to golang internal type name.
func (o FieldType) Export() (size int, typ, pkg string) {
	// Input field definition.
	//
	//   - int
	//   - int unsigned
	//   - int(10) unsigned
	var (
		in  = o.String()
		m   []string
		str string
	)

	// Return default if not configured.
	if m = regexMatchFieldType.FindStringSubmatch(in); len(m) > 0 {
		str = m[1]

		// Convert to unsigned for integer.
		//
		// `int` -> `uint`
		switch str {
		case "tinyint", "smallint", "mediumint", "int", "bigint":
			if regexMatchFieldTypeUnsigned.MatchString(in) {
				str = fmt.Sprintf(`u%s`, str)
			}
		}

		// Match mysql type.
		mt := MysqlType(str)
		if size, typ, pkg = mt.Orm(); typ != "" {
			if mt.IsInteger() {
				typ = fmt.Sprintf(`*%s`, typ)
			}
			return
		}
	}

	// Use default if not matched.
	size = 24
	pkg = ""
	typ = "any"
	return
}

// ParseOrm
// generate a tag value for field.
func (o FieldType) ParseOrm(_ *Table, column *Column) (str string) {
	str = fmt.Sprintf(`column:%s`, column.Field)

	// column.Default = strings.TrimSpace(column.Default)

	if column.Null.Yes() {
		str += fmt.Sprintf(`;default:NULL`)
	}
	return
}

func (o FieldType) String() string {
	return strings.ToLower(string(o))
}

// +---------------------------------------------------------------------------+
// | Name                                                                      |
// +---------------------------------------------------------------------------+

type Name string

func (o Name) Match(model string) bool { return model == "*" || o.String() == model }

func (o Name) Filename(prefixes ...string) (str string) {
	str = o.String()

	// Replace upper letter with underscore.
	//
	// `UserInfo` -> `_user_info`
	str = regexMatchFirstUpperLetter.ReplaceAllStringFunc(str, func(s string) string {
		if m := regexMatchFirstUpperLetter.FindStringSubmatch(s); len(m) == 2 {
			return "_" + m[1]
		}
		return s
	})

	// Replace multiple underscores with single underscore.
	//
	// `_user___info` -> `_user_info`
	str = regexMatchUnderlineMulti.ReplaceAllString(str, "_")

	// Remove heading underscores.
	//
	// `_user_info` -> `user_info`
	str = regexMatchUnderlineHeads.ReplaceAllString(str, "")

	// Prefix.
	//
	// `user_info` -> prefix_user_info
	if len(prefixes) > 0 {
		str = strings.Join(prefixes, "_") + "_" + str
	}

	// Suffix.
	//
	// `prefix_user_info` -> prefix_user_info.go
	str = str + `.go`
	return
}

func (o Name) StructName() (str string) {
	str = o.String()

	// Remove heading underscores.
	//
	// `_user_info` -> `user_info`
	str = regexMatchUnderlineHeads.ReplaceAllString(str, "")

	// Replace underscore with upper letter.
	//
	// `user_info` -> `userInfo`
	str = regexMatchUnderlineWithLetter.ReplaceAllStringFunc(str, func(s string) string {
		if m := regexMatchUnderlineWithLetter.FindStringSubmatch(s); len(m) == 2 {
			return strings.ToUpper(m[1])
		}
		return ""
	})

	// Replace first letter with upper letter.
	//
	// `userInfo` -> `UserInfo`
	str = regexMatchFirstLowerLetter.ReplaceAllStringFunc(str, func(s string) string {
		return strings.ToUpper(s)
	})
	return
}

func (o Name) String() string { return string(o) }
